#region Copyright
// 
// Copyright (C) 2008 VirtualStaticVoid <virtualstaticvoid@gmail.com>
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#endregion

using System;

namespace NAom.Core
{
  public static class PropertyAccessorExtensions
  {

    public static TValue GetProperty<TTarget, TValue>(this TTarget target, PropertyType<TValue> propertyType)
      where TTarget : class
    {
      if (target == null) throw new ArgumentNullException("target");
      if (target as IGeneratedMarker == null) throw new NAomException(SR.InvalidTargetType(target.GetType()));
      if (propertyType == null) throw new ArgumentNullException("propertyType");

      TValue value = propertyType.GetPropertyAccessor<TTarget>().GetProperty(target);
      return propertyType.RevertToDefaultValue(target, value);
    }

    public static void SetProperty<TTarget, TValue>(this TTarget target, PropertyType<TValue> propertyType, TValue value)
      where TTarget : class
    {
      if (target == null) throw new ArgumentNullException("target");
      if (target as IGeneratedMarker == null) throw new NAomException(SR.InvalidTargetType(target.GetType()));
      if (propertyType == null) throw new ArgumentNullException("propertyType");

      propertyType.GetPropertyAccessor<TTarget>().SetProperty(target, value);
    }

    public static TValue GetProperty<TTarget, TValue>(this TTarget target, IPropertyType propertyType)
      where TTarget : class
    {
      if (target == null) throw new ArgumentNullException("target");
      if (target as IGeneratedMarker == null) throw new NAomException(SR.InvalidTargetType(target.GetType()));
      if (propertyType == null) throw new ArgumentNullException("propertyType");
      if (typeof(TValue) != propertyType.DataType) throw new ArgumentException(SR.MismatchedPropertyTypeDataType(propertyType.Name, propertyType.DataType, typeof(TValue)), "propertyType");

      IInternalPropertyType internalPropertyType = (IInternalPropertyType) propertyType;
      TValue value = (TValue)internalPropertyType.PropertyAccessor.GetPropertyValue(target);
      return (TValue)internalPropertyType.RevertToDefaultValue(target, value);

    }

    public static void SetProperty<TTarget, TValue>(this TTarget target, IPropertyType propertyType, TValue value)
      where TTarget : class
    {
      if (target == null) throw new ArgumentNullException("target");
      if (target as IGeneratedMarker == null) throw new NAomException(SR.InvalidTargetType(target.GetType()));
      if (propertyType == null) throw new ArgumentNullException("propertyType");
      
      // TODO: add support for Nullable types!
      //if (typeof(TValue) != propertyType.DataType) throw new ArgumentException(SR.MismatchedPropertyTypeDataType(propertyType.Name, propertyType.DataType, typeof(TValue)), "propertyType");

      IInternalPropertyType internalPropertyType = (IInternalPropertyType)propertyType;
      
      internalPropertyType.PropertyAccessor.SetPropertyValue(target, value);
    }

    // NB: no type safety on property values
    public static object GetProperty<TTarget>(this TTarget target, IPropertyType propertyType)
      where TTarget : class
    {
      if (target == null) throw new ArgumentNullException("target");
      if (target as IGeneratedMarker == null) throw new NAomException(SR.InvalidTargetType(target.GetType()));
      if (propertyType == null) throw new ArgumentNullException("propertyType");

      IInternalPropertyType internalPropertyType = (IInternalPropertyType)propertyType;
      object value = internalPropertyType.PropertyAccessor.GetPropertyValue(target);
      return internalPropertyType.RevertToDefaultValue(target, value);
    }

    // NB: no type safety on property values
    public static void SetProperty<TTarget>(this TTarget target, IPropertyType propertyType, object value)
      where TTarget : class
    {
      if (target == null) throw new ArgumentNullException("target");
      if (target as IGeneratedMarker == null) throw new NAomException(SR.InvalidTargetType(target.GetType()));
      if (propertyType == null) throw new ArgumentNullException("propertyType");

      // TODO: add support for Nullable types!
      //if (value.GetType() != propertyType.DataType) throw new ArgumentException(SR.MismatchedPropertyTypeDataType(propertyType.Name, propertyType.DataType, typeof(TValue)), "propertyType");

      IInternalPropertyType internalPropertyType = (IInternalPropertyType)propertyType;

      internalPropertyType.PropertyAccessor.SetPropertyValue(target, value);
    }

  }
}
